package com.appspot.theonionorthenews;

import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.HashMap;
import java.util.Map;

import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.http.*;

import com.appspot.theonionorthenews.CachedNewsStorage.BadGroupException;
import com.google.appengine.api.memcache.MemcacheServiceFactory;
import com.google.appengine.api.users.UserService;
import com.google.appengine.api.users.UserServiceFactory;

/**
 * Servlet to submit new news-or-parody {@link Newstem}s to the service.
 */
@SuppressWarnings("serial")
public class NewItemServlet extends BaseServlet {

  /** Parameter for the breakout quotation. */
  private static final String QUOTATION_PARAM_NAME = "quote";

  /** Parameter for the URL to verify the quotation. */
  private static final String URL_PARAM_NAME = "url";

  /** Form replacement token for error text. */
  private static final String ERROR_TOKEN = "ERROR_TEXT";

  /** Form replacement token for quotation text. */
  private static final String QUOTATION_TOKEN = "QUOTATION_TEXT";

  /** Form replacement token for URL text by user. */
  private static final String USER_URL_TOKEN = "URL_TEXT";

  /** HTML page of submission form. */
  private String form;

  /**
   * We care about logins to know who submitted the buy-Viagra-cheap
   * "news item," so we need a user service.
   * TODO: We don't actually ban anyone yet, just know who they are.
   */
  private UserService userService;

  /**
   * Form parameters to empty strings.
   */
  private static Map<String, String> empty;

  static {
    empty = new HashMap<String, String>(3);
    String nullString = "";
    empty.put(ERROR_TOKEN, nullString);
    empty.put(QUOTATION_TOKEN, nullString);
    empty.put(USER_URL_TOKEN, nullString);
  }

  /** Initialization loads the header and footer fragments, which are held forever. */
  @Override
  public void init(ServletConfig config) throws ServletException {
    try {
      form = readStringFromResource("newitem.html");
    } catch (IOException e) {
      throw new ServletException("Could not load form", e);
    }
    userService = getUserService();
  }

  public void doGet(HttpServletRequest req, HttpServletResponse resp) throws IOException {
    if (!userService.isUserLoggedIn()) {
      // We want to know who's submitting items, so redirect to login if they
      // are anonymous:
      resp.setStatus(HttpServletResponse.SC_MOVED_TEMPORARILY);
      resp.setHeader(LOCATION,
          userService.createLoginURL(req.getRequestURL().toString()));
      return;
    }

    resp.setContentType(TEXT_HTML);
    resp.setStatus(HttpServletResponse.SC_OK);
    renderForm(resp.getWriter(), form, req.getRequestURI(), empty);
  }


  public void doPost(HttpServletRequest req, HttpServletResponse resp) throws IOException {
    if (!userService.isUserLoggedIn()) {
      // We want to know who's submitting items, so redirect to login if they
      // are anonymous:
      resp.setStatus(HttpServletResponse.SC_MOVED_TEMPORARILY);
      resp.setHeader(LOCATION,
          userService.createLoginURL(req.getRequestURL().toString()));
      return;
    }

    StringBuilder errors = new StringBuilder();
    String quote = req.getParameter(QUOTATION_PARAM_NAME);;
    if (quote == null || quote.isEmpty()) {
      errors.append("<li>Quotation must be supplied.");
    }
    String url = req.getParameter(URL_PARAM_NAME);
    if (url == null || url.isEmpty()) {
      errors.append("<li>URL must be supplied.");
    } else {
      try {
        new URL(url);
      } catch (MalformedURLException e) {
        errors.append("<li>URL must be valid.");
      }
    }
    if (errors.length() > 0) {
      errors.append("</ul>");
      Map<String, String> revise = new HashMap<String, String>(3);
      revise.put(ERROR_TOKEN, errors.toString());
      revise.put(QUOTATION_TOKEN, escapeAsHtml(quote));
      revise.put(USER_URL_TOKEN, escapeQuotes(url));

      renderForm(resp.getWriter(), form, req.getRequestURI(), revise);
      return;
    }

    // I realize that email isn't a good userid, but I really want a "useful"
    // descriptor... an opaque id isn't very helpful if I can't get the email
    // from it!
    NewsItemEntity newItem = new NewsItemEntity(quote, url, userService.getCurrentUser().getEmail());
    try {
      CachedNewsStorage store = getCachedStorage();
      newItem = store.add(newItem);

      // Show them the page as if it were "randomly" selected, using the new id
      // to find it:
      resp.setStatus(HttpServletResponse.SC_MOVED_TEMPORARILY);
      String urlById = String.format("/?%s=%s", ID_PARAM, 
          Long.toString(newItem.getId(), ID_RADIX));
      if (newItem.getId() <= 0) {
        // eventual consistency; we don't have the id answer yet!
        urlById = String.format("/?%s=%s", DRAW_PARAM, 
            Long.toString(newItem.getDrawId(), ID_RADIX));
      }
      resp.setHeader(LOCATION, urlById);
    } catch (BadGroupException e) {
      sendTextError(resp, HttpServletResponse.SC_INTERNAL_SERVER_ERROR,
          e.getMessage());
    }
  }

  protected CachedNewsStorage getCachedStorage() throws BadGroupException {
    return new CachedNewsStorage(getEntityManager(),
        MemcacheServiceFactory.getMemcacheService());
  }

  private UserService getUserService() {
    return UserServiceFactory.getUserService();
  }
}
